#ifndef __MOD_TCP__
#define __MOD_TCP__

#include <pcre.h>
#include "common.hpp"
#include "decoder.hpp"

//==========================================================
class TcpSession : public Session
{
  friend class TcpDecoder;

public:
  enum TcpStat
    {
      CLOSED = 1,
      LISTEN ,
      SYN_SENT,
      SYN_RCVD,
      SYNACK_SENT,
      SYNACK_RCVD,
      ESTABLISHED,
      CLOSE_WAIT_1,
      CLOSE_WAIT_2,
      LAST_ACK,
      FIN_WAIT_1,
      FIN_WAIT_2,
      CLOSING,
      TIME_WAIT,
    } stat[2];

  enum TcpConn
    {
      CLOSE = 0x00,
      C_EST = 0x01,
      S_EST = 0x02,
      EST   = 0x03,
      C_FIN = 0x04,
      S_FIN = 0x08,
      FIN   = 0x0C,
      C_RST = 0x10,
      S_RST = 0x20,
      RST   = 0x30,
    } conn ;

  enum TcpDir
    {
      TO_CLIENT = 0,
      TO_SERVER = 1,
    } ;
  
  DecType L5Proto;
  u_int16_t port[2];
  u_int32_t seqBase[2], seqSent[2], seqNext[2];	
  u_int32_t ackRecv[2];
  u_int16_t lenSent[2];
  // Buffer * buf[2];

  SsnDir serverDir;
  PTree<TcpSession> * tree;

//   SsnDir recoverDir;
//   u_int32_t recoverSeq;
//   u_int32_t recoverAck;
//   u_int32_t recoverLen;

  
};

//==========================================================
class TcpSessionRoot : public SessionRoot
{
public:
  PTree<Session> ssn;
};

//==========================================================
class TcpRecord : public Record
{
  friend class TcpDecoder;
  friend class TcpFilter;
  friend class TcpMpxFilter;

private:
  u_int16_t sport, dport;
  byte_t * segData;
  int segSize;
public:
  TcpRecord () : sport (0), dport (0), segData (NULL), segSize (0) {} ;  
};

//==========================================================
class TcpFilter : public NDecFilter 
{
  friend class TcpDecoder;

private:
  static const bool DEBUG = false;

  u_int16_t port;
  pcre * re;
  u_int8_t flagData, flagMask;

  enum FilterType
    {
      FT_SRC_PORT,
      FT_DST_PORT,
      FT_PCRE,
      FT_FLAGS,
    } type;

  bool setupPort (std::string &arg);
  bool setupFlags (std::string &arg);

public:
  TcpFilter (FilterType t) : port (0), re (NULL), type (t) 
  {
    switch (this->type)
      {
      case FT_SRC_PORT: this->setResultType (RT_INT); break;
      case FT_DST_PORT: this->setResultType (RT_INT); break;
      case FT_FLAGS:    this->setResultType (RT_RAW); break;
      case FT_PCRE:     this->setResultType (RT_RAW); break; 
      }
  }
  ~TcpFilter () {}

  bool setup (string &arg);
  void check (NDecResult &res);
};

//==========================================================
class TcpNode : public Node
{
};

//==========================================================
class TcpMpxFilter : public NDecMpxFilter 
{
public:
  enum FilterType
    {
      FT_NULL = 0,
      FT_SRC_PORT,
      FT_DST_PORT,
    } type;

  TcpMpxFilter (FilterType argType) : type (argType) {}
  ~TcpMpxFilter () {}
  bool setup (string &name, void * p);
  int exec (NDecResult &res);
  void * getResult (int idx);
  
private:
  PTree<NDecMpxRes> portTable;
};

//==========================================================
class TcpDecoder : public Decoder
{
private:
  static const int DEBUG;
  static const int DEBUG_TIMEOUT;
  static const int DEBUG_DECODE;
  const static int SSN_TIMEOUT = 10;

  TcpRecord rec;

public:
  TcpDecoder () {};
  ~TcpDecoder () {};
  
  Record * decode (Packet &pkt, Record &prec);
  TcpSession * fetchTcpSession (Record &prev, u_int16_t &sport, u_int16_t &dport);
  NDecFilter * makeFilter (string &name, string &arg);
  NDecMpxFilter * makeMpxFilter (string &name);
  static void timeoutSession (void * obj);
};

#endif /* __MOD_TCP__ */
